package edu.princeton.cs.algs4;

/*************************************************************************
 *  Compilation:  javac Particle.java
 *      
 *  A particle moving in the unit box with a given position, velocity,
 *  radius, and mass.
 *
 *************************************************************************/

import java.awt.Color;

import edu.princeton.cs.stdlib.StdDraw;

public class Particle {
	private final static double INFINITY = Double.POSITIVE_INFINITY;

	private double rx, ry; // position
	private double vx, vy; // velocity
	private double radius; // radius
	private double mass; // mass
	private Color color; // color
	private int count; // number of collisions so far

	// create a new particle with given parameters
	public Particle(double rx, double ry, double vx, double vy, double radius,
			double mass, Color color) {
		this.vx = vx;
		this.vy = vy;
		this.rx = rx;
		this.ry = ry;
		this.radius = radius;
		this.mass = mass;
		this.color = color;
	}

	// create a random particle in the unit box (overlaps not checked)
	public Particle() {
		rx = Math.random();
		ry = Math.random();
		vx = 0.01 * (Math.random() - 0.5);
		vy = 0.01 * (Math.random() - 0.5);
		radius = 0.01;
		mass = 0.5;
		color = Color.BLACK;
	}

	// updates position
	public void move(double dt) {
		rx += vx * dt;
		ry += vy * dt;
	}

	// draw the particle
	public void draw() {
		StdDraw.setPenColor(color);
		StdDraw.filledCircle(rx, ry, radius);
	}

	// return the number of collisions involving this particle
	public int count() {
		return count;
	}

	// how long into future until collision between this particle a and b?
	public double timeToHit(Particle b) {
		Particle a = this;
		if (a == b)
			return INFINITY;
		double dx = b.rx - a.rx;
		double dy = b.ry - a.ry;
		double dvx = b.vx - a.vx;
		double dvy = b.vy - a.vy;
		double dvdr = dx * dvx + dy * dvy;
		if (dvdr > 0)
			return INFINITY;
		double dvdv = dvx * dvx + dvy * dvy;
		double drdr = dx * dx + dy * dy;
		double sigma = a.radius + b.radius;
		double d = (dvdr * dvdr) - dvdv * (drdr - sigma * sigma);
		// if (drdr < sigma*sigma) System.out.println("overlapping particles");
		if (d < 0)
			return INFINITY;
		return -(dvdr + Math.sqrt(d)) / dvdv;
	}

	// how long into future until this particle collides with a vertical wall?
	public double timeToHitVerticalWall() {
		if (vx > 0)
			return (1.0 - rx - radius) / vx;
		else if (vx < 0)
			return (radius - rx) / vx;
		else
			return INFINITY;
	}

	// how long into future until this particle collides with a horizontal wall?
	public double timeToHitHorizontalWall() {
		if (vy > 0)
			return (1.0 - ry - radius) / vy;
		else if (vy < 0)
			return (radius - ry) / vy;
		else
			return INFINITY;
	}

	// update velocities upon collision between this particle and that particle
	public void bounceOff(Particle that) {
		double dx = that.rx - this.rx;
		double dy = that.ry - this.ry;
		double dvx = that.vx - this.vx;
		double dvy = that.vy - this.vy;
		double dvdr = dx * dvx + dy * dvy; // dv dot dr
		double dist = this.radius + that.radius; // distance between particle
													// centers at collison

		// normal force F, and in x and y directions
		double F = 2 * this.mass * that.mass * dvdr
				/ ((this.mass + that.mass) * dist);
		double Fx = F * dx / dist;
		double Fy = F * dy / dist;

		// update velocities according to normal force
		this.vx += Fx / this.mass;
		this.vy += Fy / this.mass;
		that.vx -= Fx / that.mass;
		that.vy -= Fy / that.mass;

		// update collision counts
		this.count++;
		that.count++;
	}

	// update velocity of this particle upon collision with a vertical wall
	public void bounceOffVerticalWall() {
		vx = -vx;
		count++;
	}

	// update velocity of this particle upon collision with a horizontal wall
	public void bounceOffHorizontalWall() {
		vy = -vy;
		count++;
	}

	// return kinetic energy associated with this particle
	public double kineticEnergy() {
		return 0.5 * mass * (vx * vx + vy * vy);
	}

}
